﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace NPMonitor.Helpers
{
    /// <summary>  
    /// 构造函数使用True时：单个AND有效，多个AND有效；单个OR无效，多个OR无效；混合时写在AND后的OR有效  
    /// 构造函数使用False时：单个AND无效，多个AND无效；单个OR有效，多个OR有效；混合时写在OR后面的AND有效  
    /// </summary> 
    public static class PredicateExtensions
    {
        public static Expression<Func<T, bool>> OrPredicate<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
        {
            //var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
            //return Expression.Lambda<Func<T, bool>>(Expression.Or(expression1.Body, invokedExpression), expression1.Parameters);
            if (expression1 == null)
            {
                expression1 = expression2;
            }
            else
            {
                var map = expression1.Parameters.Select((f, i) => new { f, s = expression2.Parameters[i] })
                         .ToDictionary(p => p.s, p => p.f);

                var secondBody = ParameterRebinder.ReplaceParameters(map, expression2.Body);

                var binaryExpression = Expression.OrElse(expression1.Body, secondBody);
                expression1 = Expression.Lambda<Func<T, bool>>(binaryExpression, expression1.Parameters.Single());
            }
            return expression1;
        }

        public static Expression<Func<T, bool>> AndPredicate<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
        {
            //var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
            //return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters);
            if (expression1 == null)
            {
                expression1 = expression2;
            }
            else
            {
                var map = expression1.Parameters.Select((f, i) => new { f, s = expression2.Parameters[i] })
                         .ToDictionary(p => p.s, p => p.f);

                var secondBody = ParameterRebinder.ReplaceParameters(map, expression2.Body);

                var binaryExpression = Expression.AndAlso(expression1.Body, secondBody);
                expression1 = Expression.Lambda<Func<T, bool>>(binaryExpression, expression1.Parameters.Single());
            }
            return expression1;
        }
    }

    public class ParameterRebinder : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
        {
            return new ParameterRebinder(map).Visit(exp);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            ParameterExpression replacement;
            if (map.TryGetValue(p, out replacement))
            {
                p = replacement;
            }
            return base.VisitParameter(p);
        }
    }
}
